package com.kaibes.core.base.thread;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

public class ThreadPools {

public final static String single = "single-";
    
    public static ExecutorService cachedPool, fixedPool;
    public static ScheduledExecutorService schedulePool = null;
    private static ConcurrentHashMap<String, ExecutorService> singlePoolMap = new ConcurrentHashMap<>();
    private static ConcurrentHashMap<String, ScheduledFuture<?>> futureMap = new ConcurrentHashMap<>();
    
    /**
     * 初始化线程管理器
     * @param multiple 倍数，这涉及到性能，这里以availableProcessors()作为基数乘以multiple作为fixed池的大小，需要谨慎初始化
     * 如果数值太小可能会浪费性能，如果数值太大可能会卡死，一般来说写入 availableProcessors * 2
     * 但实际上你可能会发现有很大的CPU空闲而你的程序却很卡，那你可以适当增加这个数值
     * @param corePoolSize 这是维持在周期线程池的大小，通常来说，写入0也可以，这个数值越大，CPU消耗越大，处理任务的总量速度越快
     */
    public static void initialize(int multiple, int corePoolSize) {
        if (cachedPool == null) {
            cachedPool = Executors.newCachedThreadPool(new ThreadFactory() {
                private final String name = "cached-thread-";
                private int count = 1;
                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r, name+count++);
                }
            });
            fixedPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*multiple, new ThreadFactory() {
                private final String name = "fixed-thread-";
                private int count = 1;
                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r, name+count++);
                }
            });
            schedulePool = Executors.newScheduledThreadPool(corePoolSize, new ThreadFactory() {
                private final String name = "schedule-thread-";
                private int count = 1;
                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r, name+count++);
                }
            });
        }
    }
    
    /**
     * 初始化线程管理器, multiple = 2
     */
    public static void initialize() {
        initialize(2, 2);
    }
    
    /**
     * 释放内存，对应初始化方法initialize
     */
    public static synchronized void release() {
        if (cachedPool != null) {
            if (!futureMap.isEmpty()) {
                for (ScheduledFuture<?> future : futureMap.values()) {
                    future.cancel(true);
                }
                futureMap.clear();
            }
            
            cachedPool.shutdown();
            fixedPool.shutdown();
            schedulePool.shutdown();
            
            shutDownSinglePool();
            
            cachedPool = null;
            fixedPool = null;
            schedulePool = null;
        }
    }
    
    /**
     * 创建一个单线程池
     * @param name 线程池的名字
     * @return 不会为null
     */
    public static ExecutorService createNewSinglePool(String name) {
        ExecutorService service = Executors.newSingleThreadExecutor(new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, name);
            }
        });
        singlePoolMap.put(name, service);
        return service;
    }

    /**
     * 从散列里查找一个单线程池
     * @param name 线程池的名字
     * @return 查找目标线程池，如果查找不到则返回null
     */
    public static ExecutorService findSingleService(String name) {
        return singlePoolMap.get(single + name);
    }
    
    /**
     * 从散列里检查一个单线程池
     * @param name 线程池的名字
     * @return boolean
     */
    public static boolean containSingleService(String name) {
        return singlePoolMap.containsKey(single + name);
    }

    /**
     * 在缓存线程池里执行一个任务
     * @param command 线程任务
     */
    public static void runOnCached(Runnable command) {
        if (cachedPool != null) {
            cachedPool.execute(command);
        }
    }

    /**
     * 在固定线程池里执行一个任务
     * @param command 线程任务
     */
    public static void runOnFixed(Runnable command) {
        if (fixedPool != null) {
            fixedPool.execute(command);
        }
    }

    /**
     * 这里会查找线程池并执行任务，如果线程池不存在，会尝试以默认配置创建一个线程池
     * @param name 线程池的名字
     * @param command 线程任务
     */
    public static void runOnSingle(String name, Runnable command) {
        name = single+name;
        ExecutorService service;
        if (singlePoolMap.containsKey(name)) {
            service = singlePoolMap.get(name);
        } else {
            service = createNewSinglePool(name);
        }
        service.execute(command);
    }
    public static void runOnSingle(Runnable command) {
        runOnSingle("default", command);
    }
    
    /**
     * 执行一个定时任务
     * @param period 时间间隔
     * @param command 任务内容
     * @return
     */
    public static ScheduledFuture<?> scheduleAtFixedRate(long period, Runnable command) {
        return schedulePool.scheduleAtFixedRate(command, 0, period, TimeUnit.MILLISECONDS);
    }
    
    /**
     * 执行一个定时任务，这是一个固定延时的任务，总会争取准时工作，举个例子，一个任务里面花费了10秒钟，但这不会影响到下个周期任务的启动
     * @param period 时间间隔
     * @param command 任务内容
     * @param unit 时间单位
     * @return
     */
    public static ScheduledFuture<?> scheduleAtFixedRate(long period, Runnable command, TimeUnit unit) {
        return schedulePool.scheduleAtFixedRate(command, 0, period, unit);
    }
    
    /**
     * 执行一个定时任务，这是一个固定延时的任务，总会争取准时工作，举个例子，一个任务里面花费了10秒钟，但这不会影响到下个周期任务的启动
     * @param period 时间间隔
     * @param command 任务内容
     * @param taskName 任务代号
     * @return
     */
    public static ScheduledFuture<?> scheduleAtFixedRate(long period, Runnable command, String taskCode) {
        ScheduledFuture<?> scheduledFuture = schedulePool.scheduleAtFixedRate(command, 0, period, TimeUnit.MILLISECONDS);
        futureMap.put(taskCode, scheduledFuture);
        return scheduledFuture;
    }
    
    public static ScheduledFuture<?> scheduleAtFixedRate(long delay, long period, Runnable command, String taskCode) {
        ScheduledFuture<?> scheduledFuture = schedulePool.scheduleAtFixedRate(command, delay, period, TimeUnit.MILLISECONDS);
        futureMap.put(taskCode, scheduledFuture);
        return scheduledFuture;
    }
    
    /**
     * 执行一个定时任务，这是一个相对延时的任务，会在之前的事情做完之后的间隔去做，举个例子，一个任务里面花费了10秒钟，那么下个任务执行的间隔就是10+period
     * @param period 时间间隔
     * @param command 任务内容
     * @return
     */
    public static ScheduledFuture<?> scheduleWithFixedDelay(long period, Runnable command) {
        return schedulePool.scheduleWithFixedDelay(command, 0, period, TimeUnit.MILLISECONDS);
    }
    
    public static ScheduledFuture<?> scheduleWithFixedDelay(long delay, long period, Runnable command) {
        return schedulePool.scheduleWithFixedDelay(command, delay, period, TimeUnit.MILLISECONDS);
    }
    
    /**
     * 执行一个定时任务，这是一个相对延时的任务，会在之前的事情做完之后的间隔去做，举个例子，一个任务里面花费了10秒钟，那么下个任务执行的间隔就是10+period
     * @param period 时间间隔
     * @param command 任务内容
     * @param unit 时间单位
     * @return
     */
    public static ScheduledFuture<?> scheduleWithFixedDelay(long period, Runnable command, TimeUnit unit) {
        return schedulePool.scheduleWithFixedDelay(command, 0, period, unit);
    }
    
    /**
     * 执行一个定时任务，这是一个相对延时的任务，会在之前的事情做完之后的间隔去做，举个例子，一个任务里面花费了10秒钟，那么下个任务执行的间隔就是10+period
     * @param period 时间间隔
     * @param command 任务内容
     * @param taskName 任务代号
     * @return
     */
    public static ScheduledFuture<?> scheduleWithFixedDelay(long period, Runnable command, String taskName) {
        ScheduledFuture<?> scheduledFuture = schedulePool.scheduleWithFixedDelay(command, 0, period, TimeUnit.MILLISECONDS);
        futureMap.put(taskName, scheduledFuture);
        return scheduledFuture;
    }
    
    /**
     * 执行一个定时任务，这是一个相对延时的任务，会在之前的事情做完之后的间隔去做，举个例子，一个任务里面花费了10秒钟，那么下个任务执行的间隔就是10+period
     * @param initialDelay 执行延迟
     * @param period 时间间隔
     * @param command 任务内容
     * @param taskName 任务代号
     * @return
     */
    public static ScheduledFuture<?> scheduleWithFixedDelay(long delay, long period, Runnable command, String taskName) {
        ScheduledFuture<?> scheduledFuture = schedulePool.scheduleWithFixedDelay(command, delay, period, TimeUnit.MILLISECONDS);
        futureMap.put(taskName, scheduledFuture);
        return scheduledFuture;
    }
    
    /**
     * 执行一个定时任务，这是一个相对延时的任务，会在之前的事情做完之后的间隔去做，举个例子，一个任务里面花费了10秒钟，那么下个任务执行的间隔就是10+period
     * @param period 时间间隔
     * @param command 任务内容
     * @param taskName 任务代号
     * @return
     */
    public static ScheduledFuture<?> scheduleWithFixedDelayCancelOld(long period, Runnable command, String taskName) {
        cancelScheduledTask(taskName);
        return scheduleWithFixedDelay(period, command, taskName);
    }
    
    /**
     * 执行一个定时任务，这是一个相对延时的任务，会在之前的事情做完之后的间隔去做，举个例子，一个任务里面花费了10秒钟，那么下个任务执行的间隔就是10+period
     * @param period 时间间隔
     * @param command 任务内容
     * @param taskName 任务代号
     * @return
     */
    public static ScheduledFuture<?> scheduleWithFixedDelayCancelCreate(long period, Runnable command, String taskName) {
        if (hasScheduledTask(taskName)) {
            return null;
        }
        return scheduleWithFixedDelay(period, command, taskName);
    }
    
    /**
     * 执行一个定时任务，这是一个相对延时的任务，会在之前的事情做完之后的间隔去做，举个例子，一个任务里面花费了10秒钟，那么下个任务执行的间隔就是10+period
     * @param initialDelay 执行延迟
     * @param period 时间间隔
     * @param command 任务内容
     * @param taskName 任务代号
     * @return
     */
    public static ScheduledFuture<?> scheduleWithFixedDelayCancelOld(long initialDelay, long period, Runnable command, String taskName) {
        cancelScheduledTask(taskName);
        return scheduleWithFixedDelay(initialDelay, period, command, taskName);
    }

    public static void cancelScheduledTask(String taskName) {
        ScheduledFuture<?> scheduledFuture = futureMap.get(taskName);
        if (scheduledFuture != null) {
            scheduledFuture.cancel(true);
            futureMap.remove(taskName);
        }
    }
    
    public static boolean hasScheduledTask(String taskName) {
        return futureMap.containsKey(taskName);
    }

    // 是否关闭
    public static boolean isShutDownSinglePool(String name) {
        ExecutorService service = singlePoolMap.get(name);
        if (service == null) {
            return true;
        } else {
            return service.isShutdown();
        }
    }
    
    public static synchronized void shutDownSinglePool(String name) {
        if (singlePoolMap.containsKey(name)) {
            singlePoolMap.get(name).shutdown();
            singlePoolMap.remove(name);
        }
    }
    
    public static synchronized void shutDownSinglePool() {
        if (!singlePoolMap.isEmpty()) {
            for (ExecutorService executorService : singlePoolMap.values()) {
                executorService.shutdown();
            }
            singlePoolMap.clear();
        }
    }

}
